package com.cy.jdk8.stream.demo3;

import com.google.common.collect.Lists;
import org.testng.annotations.Test;
import org.testng.collections.Maps;

import java.sql.SQLOutput;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Map;
import java.util.Optional;
import java.util.function.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class SteamDemo3 {

	@Test
	public void t3_4_1() {
		// 集合
		Lists.newArrayList(1, 2).stream();
		// 数组创建流
		Integer[] intArray = {1, 2, 34};
		Arrays.stream(intArray);
		Stream.of(intArray);
		Stream.of(1, 2, 3);
		// Map
		Map<Integer, Integer> map = Maps.newHashMap();
		map.entrySet().stream().filter(entry -> entry.getValue() > 1);
	}

	// 终结操作1  没有这个操作不会操作整个流
	@Test
	public void t3_4_3() {
		// forEach
		// Lists.newArrayList(1, 2).stream().forEach(System.out::println);
		// count
		Lists.newArrayList(1, 2).stream().count();
		//  max min
		Optional<Integer> max = Lists.newArrayList(1, 2, 3).stream().max((o1, o2) -> o2 - o1);

		// 这里的map 转换是不允许 key 重复的  但是 value 可以
		Map<Integer, String> collect = Lists.newArrayList(1, 2, 3).stream().collect(
				Collectors.toMap(Function.identity(), x->"a")
		);
		System.out.println(collect);
	}

	// 终结操作2  没有这个操作不会操作整个流
	@Test
	public void t3_4_4() {
		// 这里的map 转换是不允许 key 重复的  但是 value 可以
		Map<Integer, String> collect = Lists.newArrayList(1, 2, 3).stream().collect(
				Collectors.toMap(Function.identity(), x->"a")
		);
		System.out.println(collect);
	}

	// 终结操作3  没有这个操作不会操作整个流
	@Test
	public void t3_4_5() {
		Lists.newArrayList(1,2,3,4).stream().anyMatch( x-> x>5);
		Lists.newArrayList(1,2,3,4).stream().allMatch( x-> x>0);
		// 判断都不符合
		boolean b = Lists.newArrayList(1, 2, 3, 4).stream().noneMatch(x -> x > 10);
		// findAny 非并行流的情况下 结果 = findFirst
		Optional<Integer> any = Lists.newArrayList(1, 2, 3, 4, 5, 6, 7, 8).stream().findAny();
		// 如果不为空 就执行
		any.ifPresent(System.out::println);
	}

	// 终结操作4  没有这个操作不会操作整个流  reduce
	@Test
	public void t3_4_6() {
		// 初始化值 也可以不用 返回值会不同
		Integer reduce = Lists.newArrayList(1,2,3).stream().reduce(1, new BinaryOperator<Integer>() {
			@Override
			public Integer apply(Integer integer, Integer integer2) {
				return integer + integer2;
			}
		});
	}

// 	3.5 注意事项
// ·惰性求值(如果没有终结操作，没有中间操作是不会得到执行的)
// 	流是一次性的(一旦一个流对象经过一个终结操作后。这个流就不能再被使用)
// 	不会影响原数据(我们在流中可以多数据做很多处理。但是正常情况下是不会影响原来集合中的元素的。这往往也是我们期望的)


	// 4.Optional
	// 4.2 使用
	// 4.2.1 创建对象
	// 4.2.2 安全消费值
	// 4.2.3 获取值
	// 4.2.4 安全获取值
	// 4.2.5 过滤
	// 4.2.6 判断
	// 4.2.7 数据转换

	@Test
	public void t4(){
		// 不确定是否为空
		Integer res = null;
		// Optional.ofNullable(res).ifPresent(System.out::println);

		// 确定一定不为空 否则会空指针
		// Optional.of(res).ifPresent(System.out::println);

		Optional.empty();

		// 不存在会抛出异常的 所以不推荐
		// Optional.of(null).get();

		// res = Optional.ofNullable(res).orElse(200);
		// res = Optional.of(res).orElseGet(() -> 200);
		// 如果为空则抛出指定异常
		// try {
		// 	Optional.ofNullable(null).orElseThrow(() -> new RuntimeException("cy"));
		// } catch (Throwable throwable) {
		// 	throwable.printStackTrace();
		// }

		Optional<Integer> integer = Optional.ofNullable(res).filter(new Predicate<Integer>() {
			@Override
			public boolean test(Integer x) {
				return x > 10;
			}
		});
		System.out.println(integer.isPresent());
	}


	// 5 函数接口
// 	5.函数式接口
// 5.1 概述
// 	只有一个抽象方法的接口我们称之为函数接口。
// 	JDK的函数式接口都加上了@Functionalnterface 注解进行标识。但是无论是否加上该注解只要接口中只有一个抽象方法，都是函数式接口。

	// Consumer消费接口
	// 根据其中抽象方法的参数列表和返回值类型知道，我们可以在方法中对传入的参数进行消费。

	// Function 计算转换接口
	// 根据其中抽象方法的参数列表和返回值类型知道，我们可以在方法中对传入的参数计算或转换，把结果返回

	// Predicate 判断接口
	// 根据其中抽象方法的参数列表和返回值类型知道，我们可以在方法中对传入的参数条件判断，返回判断结果

	// Supplier 生产型接口
	// 根据其中抽象方法的参数列表和返回值类型知道，我们可以在方法中创建对象，把创建好的对象返回


	// 6.方法引用
	// 我们在使用lambda时，如果方法体中只有一个方法的调用的话(包括构造方法),我们可以用方法引用进一步简化代码,。
	// 6.1 推荐用法
	// 我们在使用lambda时不需要考虑什么时候用方法引用，用哪种方法引用，方法引用的格式是什么。
	// 我们只需要在写完lambda方法发现方法体只有一行代码，并目是方法的调用时使用快捷键尝试是否能够转换成方法引用即可。
	// 当我们方法引用使用的多了慢慢的也可以直接写出方法引用。
	// 6.2 基本格式
	// 类名或者对象名::方法名

    // 6.3.1 引用类的静态方法
	// 		其实就是引用类的静态方法
	// 格式
	// 类名::方法名
	// 		使用前提
	// 如果我们在重写方法的时候，方法体中只有一行代码，并且这行代码是调用了某个类的静态方法，并且我们把要重写的**抽象方法中所有的参数都按照顺序传入了这个静态方法中**，这个时候我们就可以引用类的静态方法。
	// 只有1行代码 是静态方法  并且抽象方法中所有的参数都按照顺序传入了这个静态方法中

	// 6.3.2 引用对象的实例方法
	// 		格式
	// 对象名::方法名
	// 		java
	// 使用前提
	// 如果我们在重写方法的时候，方法体中只有一行代码，并且这行代码是调用了某个对象的成员方法，并且我们把要重写的抽象方法中所有的参数都按照顺序传入了这个成员方法中，这个时候我们就可以引用对象的实例方法
	// 1行代码 调用对象的某个方法 ，并且我们把要重写的抽象方法中所有的参数都按照顺序传入了这个成员方法中

	// forEach(new Consumer<String>(
	// 		@override
	// 		public void accept(String name){
	// 			sb.append(name)
    // })
    //		forEach(sb::append)



	// 6.3.4 引用类的实例方法
	// 		格式
	// 类名::方法名
	// 		使用前提
	// 如果我们在重写方法的时候，方法体中只有一行代码，
	// 并且这行代码是调用了第一个参数的成员方法，并且我们把要**重写的抽象方法中剩余的所有的参数都按照顺序传入了这个成员方法中**，

	// forEach(
	// 		new UseString(){
	// 			public String use(String a,int start,int end){
	// 				a.substring(start,end)
	// 	   }
	// 	}
	// )
    //  String::substring

	// 6.3.5 构造器引用
	// 如果方法体中的一行代码是构造器的话就可以使用构造器引用。
	// 格式
	// 类名::new
	// 使用前提
	// 如果我们在重写方法的时候，方法体中只有一行代码，并且这行代码是调用了某个类的构造方法，
	// 并且我们把要重写的抽象方法中的所有的参数都按照顺序传入了这个构造方法中，这个时候我们就可以引用构造器。


	@Test
	public void t6(){
		// 这样后续操作的都是int 不会各种拆装箱
		Lists.newArrayList(1,2,3).stream().mapToInt(value -> value);
	}

}

